#pragma once

#ifndef __LIT__DISPLAY_SCENE_MANAGER__HH__
#define __LIT__DISPLAY_SCENE_MANAGER__HH__

#include <stdexcept>
#include <vector>
#include <LitCoreComponent.h>
#include <LitExceptions.h>
#include <LitVector.hpp>
#include <LitSceneSelection.h>
#include <LitMesh.h>
#include <LitEntity.h>
#include <LitLight.h>
#include <LitPlane.h>
#include <LitPhysicsManager.h>
#include <LitMeshDescriptionManager.h>
#include <LitXmlSceneManager.h>

namespace Lit
{
	LIT_API std::string trShadowTechnique( Ogre::ShadowTechnique tech );
	LIT_API Ogre::ShadowTechnique enumShadowTechnique( const std::string& tech );

	typedef MasterPtr<Movable>							MovableMasterPtr;
	typedef Vector<MovableMasterPtr>					MovableMasterPtrList;

	class LIT_API SceneManager : public MovableMasterPtrList::Listener, public CoreComponent
	{
	public:
	protected:
		MovableMasterPtrList			mMovables;
		XmlSceneManagerPtr				mXmlMgr;
		OgreCameraPtr					mCamera;
		OgreRenderWindowPtr				mWindow;
		OgreSceneManagerPtr				mSceneMgr;
		SceneSelectionPtr				mSelection;

		void		itemAdded( MovableMasterPtrList::Itor& item );
		void		itemRemoved( MovableMasterPtrList::Itor& item );
	public:
		SceneManager( Ogre::SceneManager * mgr );
		~SceneManager( );

		void initialise(OgreRenderWindowPtr win, OgreCameraPtr cam, OgreSceneManagerPtr mgr );
		bool renderOneFrame( float dt );
		void shutdown();
		
		/*****************************************************************************/	
		/************************* NAMING FUNCS **********************************/
		std::string		baseMeshName( const std::string &raw_mesh ) const;
		std::string		nextLightName( const std::string &light_name = "" ) const;
		std::string		nextEntityName(const std::string &raw_mesh) const;
		
		/*****************************************************************************/	
		/************************* SELECTION **********************************/
		void						selectAll( );
		void						selectInverse( );
		void						selectNone( );
		void						setSelected( MovablePtr obj, bool state );
		const SceneSelectionPtr&	getSelection() const;
		SceneSelectionPtr&			getSelection();
		void						setSelection( const SceneSelectionPtr& ptr );
		XmlSceneManagerPtr			xmlSceneManager();
		OgreSceneManagerPtr&		_sceneManager();
		Ogre::RaySceneQuery*							createRayQuery( const Ogre::Ray& ray, unsigned long mask = 0xFFFFFF );
		Ogre::PlaneBoundedVolumeListSceneQuery*			createVolumeQuery( const Ogre::PlaneBoundedVolume& vol, unsigned long mask = 0xFFFFFF );
		
		void destroyQuery( Ogre::SceneQuery * query );
		
		/*****************************************************************************/	
		/************************* OBJECT CREATION **********************************/
		
		template<typename ResTypePtr> ResTypePtr _createMovable( typename ResTypePtr::Ptr r, Ogre::SceneManager * mgr = NULL );

		bool isPhysicsActor( MovablePtr mov ) const;
		Physics::BodyPtr getPhysicsBody( MovablePtr mov );
		void setMovableProperties( MovablePtr mov, bool is_phys_actor=false, Physics::BodyPtr body=NULL );
		
		MeshDescriptionFilePtr getMeshDescription( const std::string &mesh_name );
		MeshDescriptionFilePtr getMeshDescription( Ogre::MeshPtr mesh );

		PlanePtr	createPlane( PlaneAxis axis, const std::string &name, const Vec2 &size = Vec2(100.0f) );
		
		Physics::BodyPtr	createActor( const std::string &name, const MeshDescriptionFilePtr& desc, Physics::WorldPtr world = Physics::WorldPtr(),  const Lit::Vec3& scale = Lit::Vec3::UNIT_SCALE, Ogre::SceneNode * parentNode=NULL, Ogre::SceneManager* sm=NULL );

		Physics::BodyPtr	createDynamicBody( Physics::WorldPtr world, const std::string &name, const std::string &mesh, Lit::Real mass, Physics::ShapeType shape=Physics::ST_BOX, Ogre::SceneNode * parentNode = NULL, Ogre::SceneManager* sm = NULL, const Lit::Vec3 & position=Lit::Vec3::ZERO, const Lit::Vec3 &scale=Lit::Vec3::UNIT_SCALE, const Lit::Quat& orientation=Lit::Quat::IDENTITY );
		Physics::BodyPtr	createStaticBody( Physics::WorldPtr world, const std::string &name, const std::string &mesh, Physics::ShapeType shape=Physics::ST_BOX, Ogre::SceneNode * parentNode = NULL, Ogre::SceneManager* sm = NULL, const Lit::Vec3 & position=Lit::Vec3::ZERO, const Lit::Vec3 &scale=Lit::Vec3::UNIT_SCALE, const Lit::Quat& orientation=Lit::Quat::IDENTITY );
		
		Physics::BodyPtr	createDynamicBody( Physics::WorldPtr world, const std::string &mesh, Lit::Real mass, Physics::ShapeType shape=Physics::ST_BOX, Ogre::SceneNode * parentNode = NULL, Ogre::SceneManager* sm = NULL, const Lit::Vec3 & position=Lit::Vec3::ZERO, const Lit::Vec3 &scale=Lit::Vec3::UNIT_SCALE, const Lit::Quat& orientation=Lit::Quat::IDENTITY );
		Physics::BodyPtr	createStaticBody( Physics::WorldPtr world, const std::string &mesh, Physics::ShapeType shape=Physics::ST_BOX, Ogre::SceneNode * parentNode = NULL, Ogre::SceneManager* sm = NULL, const Lit::Vec3 & position=Lit::Vec3::ZERO, const Lit::Vec3 &scale=Lit::Vec3::UNIT_SCALE, const Lit::Quat& orientation=Lit::Quat::IDENTITY );
		
		Physics::BodyPtr	createDynamicBody( Physics::WorldPtr world, EntityPtr getEntity, Lit::Real mass, Physics::ShapeType shape=Physics::ST_BOX, Ogre::SceneNode * parentNode = NULL, Ogre::SceneManager* sm = NULL, const Lit::Vec3 & position=Lit::Vec3::ZERO, const Lit::Vec3 &scale=Lit::Vec3::UNIT_SCALE, const Lit::Quat& orientation=Lit::Quat::IDENTITY );
		Physics::BodyPtr	createStaticBody( Physics::WorldPtr world, EntityPtr getEntity, Physics::ShapeType shape=Physics::ST_BOX, Ogre::SceneNode * parentNode = NULL, Ogre::SceneManager* sm = NULL, const Lit::Vec3 & position=Lit::Vec3::ZERO, const Lit::Vec3 &scale=Lit::Vec3::UNIT_SCALE, const Lit::Quat& orientation=Lit::Quat::IDENTITY );
		
		EntityPtr			createEntity( const std::string &mesh, Ogre::SceneNode * parentNode = NULL, Ogre::SceneManager* sm = NULL, const Lit::Vec3 & position=Lit::Vec3::ZERO, const Lit::Vec3 &scale=Lit::Vec3::UNIT_SCALE, const Lit::Quat& orientation=Lit::Quat::IDENTITY );
		EntityPtr			createEntity( const std::string &name, const std::string &mesh, Ogre::SceneNode * parentNode = NULL, Ogre::SceneManager* sm = NULL, const Lit::Vec3 & position=Lit::Vec3::ZERO, const Lit::Vec3 &scale=Lit::Vec3::UNIT_SCALE, const Lit::Quat& orientation=Lit::Quat::IDENTITY );

		LightPtr			createLight( const std::string &name="", Light::Type type_=LT_POINT, const Ogre::Vector3& position=Ogre::Vector3::ZERO, const Ogre::Vector3& direction=Ogre::Vector3::NEGATIVE_UNIT_Y );
		
		/*****************************************************************************/	
		/************************* OBJECT SEARCH FUNCS **********************************/
		MovableMasterPtrList::Itor _find( const std::string &name, MovableType type );
		MovableMasterPtrList::ConstItor _find( const std::string &name, MovableType type ) const;
		
		/*****************************************************************************/	
		/************************* OBJECT PRESENCE TESTS **********************************/

		bool		hasMovable( const std::string &name ) const;
		bool		hasEntity( const std::string &name ) const;
		bool		hasPlane( const std::string &name ) const;
		bool		hasLight( const std::string &name ) const;

		/*****************************************************************************/		
		/************************* OBJECT RETRIEVAL **********************************/

		MovablePtr	getMovable( const std::string &name );
		EntityPtr	getEntity(const std::string &name);
		PlanePtr	getPlane(const std::string &name);
		LightPtr	getLight(const std::string &name);
		
		/*****************************************************************************/	
		/************************* OBJECT REMOVAL **********************************/

		void		clearScene();
		void		clearSelection( bool unselect=true );

		void		removeLight( const std::string &name );
		void		removeEntity( const std::string &name );
		void		removePlane( const std::string &name );

		void		removeMovable( Movable* mov );
		/*****************************************************************************/	

	};

	typedef RefPtr<SceneManager> SceneManagerPtr;
};

#endif